'use strict'
var http = require('http');
var fs = require('fs');

const fsp = fs.promises;

function parseBodyHeaders(data) {
    let headers = {}
    // \r\ncontent-type:multipart/form-data; boundary=-----34294329
    let dsplit = data.split('\r\n').filter(p => p.length >0)
    
    let ind;
    let k;
    for(let d of dsplit){
        ind = d.indexOf(':');

        if(ind<=0) continue;

        k=d.substring(0,ind).toLowerCase();

        headers[k] = d.substring(ind+1).trim();

    }


    let cpd = headers['content-disposition'];

    // 按照;分割成数组，第一个元素form-data去掉
    let cList = cpd.split('; ').slice(1);
    let name,filename;
    for (let a of cList) {
        ind = a.indexOf('filename="')
        if(ind >= 0){
            filename = a.substring(ind+10, a.length-1)
        }else{
            name = a.substring(6,a.length - 1)
        }
    }
    return {
        headers,
        name,
        filename
    }
}
/**
 * 这部分是注释，vs code中会添加对参数的描述
 * @param {Buffer} bodyData 原始的Body数据
 * @param {Object} reqHeader req.headers
 */

function parseBody(bodyData,reqHeader) {

    // content-type:multipart/form-data; boundary=-----34294329
    let ctype = reqHeader['content-type'];
    // 拿到分界线boundary
    let bdy = ctype.substring(ctype.indexOf('=')+1);//拿到boundary的值开始位置
    // body在构造数据会在boundary前面再加两个减号
    // 数据的结尾会在上面的那个后面再加两个减号
    // 数据中用于分割文件的分割线
    // bdy = `--${bdy}\r\n`;
    let crlf_bdy = `\r\n--${bdy}`;
    // 从偏移crlf_bdy的长度的位置开始查找下一个分界线的位置
    let data_end_index = bodyData.indexOf(crlf_bdy,crlf_bdy.length);

    // body消息数据中header部分的最后索引
    let header_end_index = bodyData.indexOf('\r\n\r\n',bdy.length);

    let header_data = bodyData.toString('utf-8',
                                    crlf_bdy.length,
                                    header_end_index);
    // 解析body数据中的消息头
    let hd = parseBodyHeaders(header_data);
    
    let fileinfo = {
        start:header_end_index+4,
        end:data_end_index,
    }
    fileinfo.length = fileinfo.end - fileinfo.start;

    return {
        name:hd.name,
        filename:hd.filename,
        headers:hd.headers,
        start:fileinfo.start,
        end:fileinfo.end,
        length:fileinfo.length
    }
}

function parseExtName(filename) {
    if (filename.length < 2) return '';

    let namesplit = filename.split('.')
                        .filter(p => p.length > 0)

    if (namesplit.length < 2) return '';

    return `.${namesplit[namesplit.length - 1]}`
}

let routerTable = {

    GET:{
        '/upload':async (ctx)=>{
            let stm = fs.createReadStream('./upload.html');
            stm.pipe(ctx.res);
            stm.on('end',()=>{
                ctx.end();
            })
        },
    },
    POST:{
        '/upload':async (ctx)=>{//表单使用了POST方式提交了数据，所以采用这种处理
            console.log(ctx.files)
            let finfo = ctx.files

            let filename = `${Date.now()}-`
                            +`${parseInt(Math.random()*100000)+100000}`//给文件创建一个唯一别名
                            +parseExtName(finfo.filename)

            let fh = await fsp.open(filename,'w+');

            

            let fret = await fh.write(ctx.rawBody,
                                    finfo.start,
                                    finfo.length,
                                    0)
            // 关闭打开的文件
            fh.close();
            // 返回写入的字节数，string或buffer类型，不能是其他类型。
            
            
            ctx.end(`${fret.bytesWritten}`);
            
        }
    }
}
// **-------------------------------------START SERVER------------------------------*/

let serv = http.createServer();
serv.on('request',(req,res)=>{
    let rm = routerTable[req.method];

    // 利用参数与路径间的?来切分字符串
    let usplit = req.url.split("?");
    let pathname = usplit[0];
    let querystring = usplit[1] ||'';
    // 这里如果上传了参数就会导致无法访问，所以要用上面对url进行切分来解决这个问题
    if(!rm || !rm[pathname]){
        res.statusCode = 404;
        res.end("page not found")
        return
    }


    // ctx is context ,创建请求上下文对象
    let ctx = {
        req:req,
        res:res,
        method:req.method,
        headers:req.headers,
        end:(data,encoding='utf8')=>{
            res.end(data, encoding);
        },
        write:(data,encoding='utf8') =>{
            res.write(data,encoding)
        },
        path:pathname,
        querystring:querystring,//未经过解析的访问字符串
        rawBody:null,//raw常用于表示生的、未处理的，这里就是未处理的body数据
    }

    let bufferList =[];
    let totalLength = 0;

    if('PD'.indexOf(req.method[0]) >= 0){
        req.on('data',chunk => {
            totalLength += chunk.length;
            bufferList.push(chunk);
        })
    }
    else{
        req.on('data',chunk => {
            res.statusCode = 400;
            res.end();
        })
    }

    req.on('end',() => {
        if (bufferList.length > 0) {
            ctx.rawBody = Buffer.concat(bufferList , totalLength);
            bufferList = [];
        }
      
    
        let ctype = ctx.headers['content-type']||''
        if (ctx.rawBody && ctype.indexOf('multipart/form-data')===0) {
            ctx.files = parseBody(ctx.rawBody,ctx.headers)
            
        }
        rm[pathname](ctx)
    })
})
serv.listen(3456);

